/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package at.alexanderpehm.ahnentafel.dao;

import at.alexanderpehm.ahnentafel.dao.exceptions.NonexistentEntityException;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.Query;
import javax.persistence.EntityNotFoundException;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import at.alexanderpehm.ahnentafel.persistence.Person;
import java.util.ArrayList;
import java.util.List;
import at.alexanderpehm.ahnentafel.persistence.Event;

/**
 *
 * @author alex
 */
public class PersonJpaController {

    public PersonJpaController() {
        emf = Persistence.createEntityManagerFactory("at.alexanderpehm.ahnentafel.persistencePU");
    }
    private EntityManagerFactory emf = null;

    public EntityManager getEntityManager() {
        return emf.createEntityManager();
    }

    public void create(Person person) {
        if (person.getChildren() == null) {
            person.setChildren(new ArrayList<Person>());
        }
        if (person.getEvents() == null) {
            person.setEvents(new ArrayList<Event>());
        }
        EntityManager em = null;
        try {
            em = getEntityManager();
            em.getTransaction().begin();
            Person father = person.getFather();
            if (father != null) {
                father = em.getReference(father.getClass(), father.getId());
                person.setFather(father);
            }
            Person mother = person.getMother();
            if (mother != null) {
                mother = em.getReference(mother.getClass(), mother.getId());
                person.setMother(mother);
            }
            List<Person> attachedChildren = new ArrayList<Person>();
            for (Person childrenPersonToAttach : person.getChildren()) {
                childrenPersonToAttach = em.getReference(childrenPersonToAttach.getClass(), childrenPersonToAttach.getId());
                attachedChildren.add(childrenPersonToAttach);
            }
            person.setChildren(attachedChildren);
            List<Event> attachedEvents = new ArrayList<Event>();
            for (Event eventsEventToAttach : person.getEvents()) {
                eventsEventToAttach = em.getReference(eventsEventToAttach.getClass(), eventsEventToAttach.getId());
                attachedEvents.add(eventsEventToAttach);
            }
            person.setEvents(attachedEvents);
            em.persist(person);
            if (father != null) {
                father.getChildren().add(person);
                father = em.merge(father);
            }
            if (mother != null) {
                mother.getChildren().add(person);
                mother = em.merge(mother);
            }
            for (Person childrenPerson : person.getChildren()) {
                childrenPerson.getChildren().add(person);
                childrenPerson = em.merge(childrenPerson);
            }
            for (Event eventsEvent : person.getEvents()) {
                eventsEvent.getPersons().add(person);
                eventsEvent = em.merge(eventsEvent);
            }
            em.getTransaction().commit();
        } finally {
            if (em != null) {
                em.close();
            }
        }
    }

    public void edit(Person person) throws NonexistentEntityException, Exception {
        EntityManager em = null;
        try {
            em = getEntityManager();
            em.getTransaction().begin();
            Person persistentPerson = em.find(Person.class, person.getId());
            Person fatherOld = persistentPerson.getFather();
            Person fatherNew = person.getFather();
            Person motherOld = persistentPerson.getMother();
            Person motherNew = person.getMother();
            List<Person> childrenOld = persistentPerson.getChildren();
            List<Person> childrenNew = person.getChildren();
            List<Event> eventsOld = persistentPerson.getEvents();
            List<Event> eventsNew = person.getEvents();
            if (fatherNew != null) {
                fatherNew = em.getReference(fatherNew.getClass(), fatherNew.getId());
                person.setFather(fatherNew);
            }
            if (motherNew != null) {
                motherNew = em.getReference(motherNew.getClass(), motherNew.getId());
                person.setMother(motherNew);
            }
            List<Person> attachedChildrenNew = new ArrayList<Person>();
            for (Person childrenNewPersonToAttach : childrenNew) {
                childrenNewPersonToAttach = em.getReference(childrenNewPersonToAttach.getClass(), childrenNewPersonToAttach.getId());
                attachedChildrenNew.add(childrenNewPersonToAttach);
            }
            childrenNew = attachedChildrenNew;
            person.setChildren(childrenNew);
            List<Event> attachedEventsNew = new ArrayList<Event>();
            for (Event eventsNewEventToAttach : eventsNew) {
                eventsNewEventToAttach = em.getReference(eventsNewEventToAttach.getClass(), eventsNewEventToAttach.getId());
                attachedEventsNew.add(eventsNewEventToAttach);
            }
            eventsNew = attachedEventsNew;
            person.setEvents(eventsNew);
            person = em.merge(person);
            if (fatherOld != null && !fatherOld.equals(fatherNew)) {
                fatherOld.getChildren().remove(person);
                fatherOld = em.merge(fatherOld);
            }
            if (fatherNew != null && !fatherNew.equals(fatherOld)) {
                fatherNew.getChildren().add(person);
                fatherNew = em.merge(fatherNew);
            }
            if (motherOld != null && !motherOld.equals(motherNew)) {
                motherOld.getChildren().remove(person);
                motherOld = em.merge(motherOld);
            }
            if (motherNew != null && !motherNew.equals(motherOld)) {
                motherNew.getChildren().add(person);
                motherNew = em.merge(motherNew);
            }
            for (Person childrenOldPerson : childrenOld) {
                if (!childrenNew.contains(childrenOldPerson)) {
                    childrenOldPerson.getChildren().remove(person);
                    childrenOldPerson = em.merge(childrenOldPerson);
                }
            }
            for (Person childrenNewPerson : childrenNew) {
                if (!childrenOld.contains(childrenNewPerson)) {
                    childrenNewPerson.getChildren().add(person);
                    childrenNewPerson = em.merge(childrenNewPerson);
                }
            }
            for (Event eventsOldEvent : eventsOld) {
                if (!eventsNew.contains(eventsOldEvent)) {
                    eventsOldEvent.getPersons().remove(person);
                    eventsOldEvent = em.merge(eventsOldEvent);
                }
            }
            for (Event eventsNewEvent : eventsNew) {
                if (!eventsOld.contains(eventsNewEvent)) {
                    eventsNewEvent.getPersons().add(person);
                    eventsNewEvent = em.merge(eventsNewEvent);
                }
            }
            em.getTransaction().commit();
        } catch (Exception ex) {
            String msg = ex.getLocalizedMessage();
            if (msg == null || msg.length() == 0) {
                Long id = person.getId();
                if (findPerson(id) == null) {
                    throw new NonexistentEntityException("The person with id " + id + " no longer exists.");
                }
            }
            throw ex;
        } finally {
            if (em != null) {
                em.close();
            }
        }
    }

    public void destroy(Long id) throws NonexistentEntityException {
        EntityManager em = null;
        try {
            em = getEntityManager();
            em.getTransaction().begin();
            Person person;
            try {
                person = em.getReference(Person.class, id);
                person.getId();
            } catch (EntityNotFoundException enfe) {
                throw new NonexistentEntityException("The person with id " + id + " no longer exists.", enfe);
            }
            Person father = person.getFather();
            if (father != null) {
                father.getChildren().remove(person);
                father = em.merge(father);
            }
            Person mother = person.getMother();
            if (mother != null) {
                mother.getChildren().remove(person);
                mother = em.merge(mother);
            }
            List<Person> children = person.getChildren();
            for (Person childrenPerson : children) {
                childrenPerson.getChildren().remove(person);
                childrenPerson = em.merge(childrenPerson);
            }
            List<Event> events = person.getEvents();
            for (Event eventsEvent : events) {
                eventsEvent.getPersons().remove(person);
                eventsEvent = em.merge(eventsEvent);
            }
            em.remove(person);
            em.getTransaction().commit();
        } finally {
            if (em != null) {
                em.close();
            }
        }
    }

    public List<Person> findPersonEntities() {
        return findPersonEntities(true, -1, -1);
    }

    public List<Person> findPersonEntities(int maxResults, int firstResult) {
        return findPersonEntities(false, maxResults, firstResult);
    }

    private List<Person> findPersonEntities(boolean all, int maxResults, int firstResult) {
        EntityManager em = getEntityManager();
        try {
            CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
            cq.select(cq.from(Person.class));
            Query q = em.createQuery(cq);
            if (!all) {
                q.setMaxResults(maxResults);
                q.setFirstResult(firstResult);
            }
            return q.getResultList();
        } finally {
            em.close();
        }
    }

    public Person findPerson(Long id) {
        EntityManager em = getEntityManager();
        try {
            return em.find(Person.class, id);
        } finally {
            em.close();
        }
    }

    public int getPersonCount() {
        EntityManager em = getEntityManager();
        try {
            CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
            Root<Person> rt = cq.from(Person.class);
            cq.select(em.getCriteriaBuilder().count(rt));
            Query q = em.createQuery(cq);
            return ((Long) q.getSingleResult()).intValue();
        } finally {
            em.close();
        }
    }

}
